#include "ScriptSubsystem.h"

#include "ScriptRuntime/ByteCode/ByteCodeFile.h"
#include "ScriptRuntime/ByteCode/ByteCodeReader.h"
#include "ScriptRuntime/ByteCode/ByteCodeLoader.h"

#include "ScriptRuntime/Type/GSharpClass.h"
#include "ScriptRuntime/Type/ScriptContent.h"
#include "ScriptRuntime/Type/ScriptFunction.h"


#include "ScriptRuntime/ScriptInnerType.h"

#include "Reflection/FunctionList.h"

#include "ScriptRuntime/Type/GSharpFieldProperty.h"

#include "ScriptRuntime/Interpreter/RuntimeScriptFile.h"

#include "ScriptRuntime/Debug/Breakpoint.h"
#include "ScriptRuntime/Debug/DebugServer.h"

#include "Async/Async.h"
#include "Misc/Assert.h"

static RScriptSubsystem* GScriptSubsystem = nullptr;

RScriptSubsystem *RScriptSubsystem::Get()
{
    assert(GScriptSubsystem && "GScriptSubsystem is not initialized");
    return GScriptSubsystem;
}

void RScriptSubsystem::Initialize()
{
    GScriptSubsystem=this;


    //Interpreter= std::make_shared<CScriptInterpreter>();
}

void RScriptSubsystem::Deinitialize()
{
    GScriptSubsystem=nullptr;

#ifdef DEBUG_GSHARP
    delete DebugServer;
    DebugServer=nullptr;
#endif

}

void RScriptSubsystem::LoadByteCode(const TVector<uint8_t> &InByteCode,const CString& ScriptLibraryName)
{
    std::shared_ptr<CByteCodeFile> ByteCodeFile=std::make_shared<CByteCodeFile>();
    ByteCodeFile->ByteCode=InByteCode;

    LoadScript(ByteCodeFile,ScriptLibraryName);
}

void RScriptSubsystem::LoadScript(std::shared_ptr<CByteCodeFile> InByteCodeFile,const CString& ScriptLibraryName)
{
    CByteCodeLoader ByteCodeLoader= CByteCodeLoader();
    ByteCodeLoader.LoadByteCodeFile(InByteCodeFile,ScriptLibraryName);
}

CAny RScriptSubsystem::InvokeStaticFunction( const CString &InClassFullName, const CString &InFunctionName, const TVector<CAny> &InArgs)
{
    auto Type=
        CType::FindType(InClassFullName);

    GASSERT(Type,CString(U"找不到类型： ")+InClassFullName);
    auto Function = Type->Functions->FindFunction(InFunctionName);

    assert(Function && "Function not found");

    return Function->Invoke(InArgs);
}

void RScriptSubsystem::StartDebugServer(uint16_t InPort)
{
    assert(!DebugServer && "DebugServer already started");
    CDebugServer::Port=InPort;
    DebugServer=new CDebugServer();
}

void RScriptSubsystem::StopDebugServer()
{
    assert(DebugServer && "DebugServer not started");
    delete DebugServer;
    DebugServer=nullptr;
}

bool RScriptSubsystem::IsDebugServerStarted() const
{
    return DebugServer!=nullptr;
}

bool RScriptSubsystem::IsDebuggerAttached() const
{
    return DebugServer
        && DebugServer->IsDebuggerAttached();
}

void RScriptSubsystem::MarkAutoExitAppWhenDebuggerDetached() const
{
    assert(DebugServer && "DebugServer not started");
    DebugServer->MarkAutoExitAppWhenDebuggerDetached();
}

void RScriptSubsystem::SetScriptFiles(const TVector<std::shared_ptr<CRuntimeScriptFile>> &InScriptFiles)
{
    AllScriptFiles=InScriptFiles;
}

void RScriptSubsystem::SetBreakpoint(const SPath & InFilePath, uint32_t InLine, CBreakpointSlot::EBreakpointState NewState )
{
    //find the script file
    TOptional<uint64_t> ScriptFileIndex=AllScriptFiles.FindBy([&](const std::shared_ptr<CRuntimeScriptFile> &InScriptFile)
    {
        return InScriptFile->FilePath.IsSameWith(InFilePath);
    });

    assert(ScriptFileIndex.HasValue() && "Script file not found");

    auto ScriptFile =AllScriptFiles[ScriptFileIndex.GetValue()];

    if(!ScriptFile->BreakpointSlots[InLine])
    {
        //cant place breakpoint here
        return ;
    }

    ScriptFile->BreakpointSlots[InLine]->State=NewState;

}

TVector<std::shared_ptr<CBreakpointSlot>> RScriptSubsystem::GetAllBreakpoints() const
{
    TVector<std::shared_ptr<CBreakpointSlot>> Breakpoints;

    for(auto& ScriptFile:AllScriptFiles)
    {
        for(auto& Breakpoint:ScriptFile->BreakpointSlots)
        {
            if(Breakpoint && Breakpoint->State!=CBreakpointSlot::EBreakpointState::NotSet)
            {
                Breakpoints.Add(Breakpoint);
            }
        }
    }

    return Breakpoints;
}

TVector<std::shared_ptr<CFunctionInterpreter>> &RScriptSubsystem::GetScriptCallStack()
{
    return ScriptCallStack;
}

void RScriptSubsystem::WaitBreakpointOver()
{
    InterpreterState = EState::HitingBreakpoint;
    OnHitBreakpoint.ExecuteIfBound();
    while(true)
    {
        CAsyncSystem::Get()->DoMainThreadWork(); //in case the debug server cant accept new request

        if(InterpreterState==EState::Running)
        {
            break;
        }
    }

}

void RScriptSubsystem::Continue()
{
    InterpreterState = EState::Running;
}

bool RScriptSubsystem::IsHitingBreakpoint() const
{
    return InterpreterState==EState::HitingBreakpoint;
}

std::shared_ptr<CRuntimeScriptFile> RScriptSubsystem::GetLoadedFile(const SPath &InFilePath)
{
    TOptional<uint64_t> ScriptFileIndex=AllScriptFiles.FindBy([&](const std::shared_ptr<CRuntimeScriptFile> &InScriptFile)
    {
        return InScriptFile->FilePath.IsSameWith(InFilePath);
    });

    if(!ScriptFileIndex.HasValue())
    {
        return nullptr;
    }

    return AllScriptFiles[ScriptFileIndex.GetValue()];
}

TVector<std::shared_ptr<CRuntimeScriptFile>> RScriptSubsystem::GetLoadedFiles() const
{
    return AllScriptFiles;
}
